
#ifndef __SEARCHREQUEST_H__
#define __SEARCHREQUEST_H__

#include "Common/NetworkProcessor.h"

namespace SPlayer
{
    namespace Network
    {
        using namespace System;
        using namespace System::Net;
        using namespace System::Net::Sockets;
        using namespace System::Collections;
        using namespace System::Collections::Generic;
        using namespace SPlayer;
        using namespace System::IO;

        value class SearchResultItem
        {
        public:
            String ^title;
            long long playLength;
            bool hasLyric;
        };

        enum class SearchRequestState
        {
            Ready, /* initialize state, not sent yet.*/
            Sent,
            Received,
            Error
        };

        ref class SearchRequestEventDispatcher
        {
        public:
            Action ^onReceived;
            Action ^onError;
        };
        ref class SearchRequest
        {
        public:
            SearchRequest(String ^keywordString) : keywordString(keywordString)
            { }

            void send()
            {
                socket = gcnew NetworkSocket;
                socket->eventDispatcher->onConnectionFinished =
                    gcnew Action(this, &SearchRequest::onConnected);
                socket->connectAsync();
            }
            void abort()
            {
                if (state == SearchRequestState::Sent)
                {
                    state = SearchRequestState::Error;
                    // abort, don't call error
                    //Function::InvokeSync(eventDispatcher->onError);
                }
                if (socket)
                {
                    socket->Close();
                }
                if (processor)
                {
                    processor->close();
                }
            }
        protected:
            void onConnected()
            {
                if (socket->Connected)
                {
                    processor = gcnew NetworkProcessor(socket);

                    processor->eventDispatcher->onParamReceived +=
                        gcnew Action(this, &SearchRequest::onParamReceived);
                    processor->eventDispatcher->onDataReceiveFinished +=
                        gcnew Action(this, &SearchRequest::onDataAllReceived);
                    processor->eventDispatcher->onError +=
                        gcnew Action<Exception ^>(this, &SearchRequest::onError);
                    processor->eventDispatcher->onClosed +=
                        gcnew Action(this, &SearchRequest::onClosed);

                    auto params = processor->sendParams;
                    params->add("Request", "SearchMusic");
                    params->add("KeywordString", keywordString);
                    processor->sendAsync();

                    state = SearchRequestState::Sent;
                }
                else if (state != SearchRequestState::Error)
                {
                    state = SearchRequestState::Error;
                    Function::InvokeSync(eventDispatcher->onError);
                }
            }
            void onParamReceived()
            {
            }
            void onDataAllReceived()
            {
                auto data = processor->receivedData;
                MemoryStream ^memstream = gcnew MemoryStream(data);
                BinaryReader ^reader = gcnew BinaryReader(memstream);
                memstream->Position = 0;
                while (memstream->Position < data->Length)
                {
                    int length = reader->ReadInt32();
                    auto arr = gcnew array<unsigned char>(length);
                    reader->Read(arr, 0, arr->Length);
                    
                    auto params = NetworkParamCollection::fromString(System::Text::Encoding::UTF8->GetString(arr));
                    SearchResultItem i;
                    i.title = params["Title"];
                    i.playLength= params["PlayLength"];
                    i.hasLyric= !!params["HasLyric"];
                    resultItems->Add(i);
                }
                delete memstream;
                delete reader;
                processor->close();

                state = SearchRequestState::Received;
                Function::InvokeSync(eventDispatcher->onReceived);
            }
            void onClosed()
            {
                if (state == SearchRequestState::Sent)
                {
                    state = SearchRequestState::Error;
                    Function::InvokeSync(eventDispatcher->onError);
                }
            }
            void onError(System::Exception ^e)
            {
                processor->close();
            }
        public:
            String ^keywordString;
            SearchRequestState state = SearchRequestState::Ready;
            List<SearchResultItem> ^resultItems = gcnew List<SearchResultItem>;
            SearchRequestEventDispatcher ^eventDispatcher = gcnew SearchRequestEventDispatcher;
        private:
            NetworkSocket ^socket = nullptr;
            NetworkProcessor ^processor = nullptr;
        };

    }
}

#endif // __SEARCHREQUEST_H__